package md.specialEqp.inspect;

import com.querydsl.core.annotations.QueryInit;
import lombok.*;
import md.cm.unit.Unit;
import md.specialEqp.BusinessCat_Enum;
import md.specialEqp.Eqp;
import md.specialEqp.Report;
import org.fjsei.yewu.aop.hibernate.UnitNameBinder;
import org.fjsei.yewu.aop.hibernate.NodeIdBinder;
import md.system.User;
import org.fjsei.yewu.filter.SimpleReport;
import org.fjsei.yewu.filter.Uunode;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.search.engine.backend.types.Aggregable;
import org.hibernate.search.engine.backend.types.Sortable;
import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.PropertyBinderRef;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.*;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import java.util.*;

//一个设备ID+一个任务ID只能做一个ISP（正常的ispID;作废删除的记录资料另外再做信息追溯）
//唯一性索引UniqueConstraint不一定生效，必须确保现有表中数据都满足约束后，JPA才能成功建立唯一索引！。
//针对graphQL关联查询的安全问题，考虑把关对其他或低权限用户不可见的字段独立出来，JPA　三种继承映射策略。
//比如ISP extends ISPbase{ISPbase字段低保密性}，ISP可额外添加保密性要求高的其他字段，代码配置graphql返回对象类型正常都用ISPbase替换ISP。
//责任工程师审核：同时收费.Task->INVC明细金额最终确认人{收钱不合理退回}；CURR_NODE= [{id:'101',text:'报告编制'},{id:'102',text:'责任工程师审核'}；


/** 检验记录 =历史报告入口管理对象。冷数据长期保存搜索报告用的。
 * IspEs对应于Isp的搜索引擎模型。有了报告编号之后就允许搜索Isp了。
 * 业务记录， 监检或定期检验等的或甚至检测工作情况。 任务短期功能转移Detail去，退化为历史报告功能。
 * Isp不直接关联Task，要去Detail中找Task;
 * 初始化状态=其实也可看做 每个Eqp子任务。
 * 检验工作的流转审查，本次检验工作的关键记录(详细数据除外)，监察关心的关键字段。
 * 本次检验决定的结论/法定应当的下次检验日期。
 * 对接旧平台/外部检验系统，历史报告检验/历史记录。
 TB_ISP_MGE像个大杂烩？TB_ISP_DET参数，报告和流转,主要是记录状态的，DATA_PATH代表报告纸,Isp代表作业成果。
 * 分项报告|主报告的流转审核打印等人员状态日期：直接放到Report模型中；
 *制造监检考虑从Isp中恢复已有的数据来复用。制造监检没有关联设备，但有单位,业务类型，附带设备类别指示就是没有Eqp,出厂编号声明范围。
 * Isp初始化时机提前了，Task任务指定设备时刻就要生成新的Isp,所以回转余地较大，实体模型含义有所变化。
 * 制造监检和水质报告这样没有Eqp的如何还原报告证书的真伪？只能从报告号和证书序列号来追溯了,Isp.repNo来定位。
 * Task底下Isp任务流转步调不一致导致，部分提前出报告，同一个Task底下的Isp允许直接拆分成两个Task，所有的Isp全部终结了关联的Task才能终结处理。
 * Isp不能简单流转终结，对于所属Task的最后一个等待终结Isp必须确保Task的收入发票回款义务完成，同一Task底下先前Isp的终结在审批人审批环节要留意发票回款进度卡住流转进度。
 * Isp审核环节：主报告审核人要明确收费金额明细账，分项报告主报告要明确报告的下结论和输入数据和报告格式内容。
 * 校核人才有权流转报告到审核，其它检验员只能编制报告；全部检验员都要签字，校核人也要签字。校核人只能一个也包含在全部检验员之中。
 * 责任人=项目负责人负责Task的收费细节生成设置。 责任人派工确定校核人检验员，各个分项报告的检验员校核人{不改的自动继承主报告}。
 * Report分项和收费的关系，Task重新计算收费，Report中可能提取部分参数{负责人在前端提供参数给计费逻辑},审核人检查收费依据，审批人依照收费回款进度卡住。
 * Isp是在Task初始化选设备时刻就添加好的。
 * 同步旧的检验平台 "LAST_ISP_ID1" "最后一次检验流水号1", 监察平台："LAST_ISP_COD1", "最后一次检验流水号1"NUMBER
 * ispDate用于清理过期数据(最长保留期限外)。
 * 本实体表数据量太大？考虑分离 Isp近期业务表+ Isp过气历史表？按照检验终结日期终结超过365天的分开;业务量统计要靠Task任务表。
 * 考虑Isp进入ES搜索引擎单独为no报告编号快速搜索对应的Isp.ID来提升访问历史Isp报告的速度,Isp过气历史表=>报告存档的入口URL。
 * 【限制】不同平台之间只能使用编码no等KEY字段而不能直接用实体表的ID访问。no+设备oid;no+设备cod;来定位特定的历史Isp报告。
 * Isp是所有的历史检验和报告入口：根据rows记录数据量预测规划，可能需要应用分片技术；
 * 关联Eqp查询很慢 name="isp_saved1",
 *光光no还不能唯一性？ where isp0_.no='MAN_AQw001T' and dev_id='f62e0d16-102e-478c-82fd-aa3372edbf4a'::uuid
 * 唯一性索引"dev_id", "no"是因为旧报告初始化。唯一性索引"no", "ispu_id"是因为业务编号规范要求。
 * 手动删除Isp时，务必首先删除清理报告关联对象，分2步骤，避免给数据库太多压力。
 * Isp数据库表的全表同步到IspES搜索引擎库，附带不定期全做reindex，正常HibernateSearch可确保两个哭的同步；正常应当通过ES搜索，【杜绝】尽可能减少直接从数据库Isp做查询过滤(id关联除外)。
 */


@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder=true)
@Entity
//@Table( )对照的 SecondaryTable or SecondaryTables
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "Fast")
@Indexed
public class Isp implements Uunode {
  /* @Id   #mySQL时用的
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;*/

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID id;

    /**业务信息: 附加收费参数 等短期状态参数。实际上算Isp_扩充表，有些场合Detail bus直接替换原来Isp isp的位置。
     * 前端【从检验入手】主页面：进入查询检验任务的列表：必须都是Detail bus!=null的才算。过时Isp不需要查询。有点像继承类extends?实体模型;
     * 必须不定期清空字段bus=null，来提供该常用的工作页面查询性能，已经终结的任务就该清除。
     * 若dev==null的检验业务，就必须依赖Detail弥补上。若dev=null那么bus必须不为null，其bus.type标记=设备种类。
     * 水质报告 安全阀 制造报告 的定位标志。
     * 检验现场每一次检验都会变动的 其余字段。
     * 冷热数据分离方式之一： 扩充的字段，检验业务工作细节表。过期的冷数据其bus字段对应存储可舍弃。
     * 收费的信息安放在Detail中。非过期的热数据必须有bus。 冷数据bus=null；
     * 正常是1 Isp:1 Detail; 可是Detail属于短期可能抛弃,而Isp保存时间很长。
     * 正常Eqp台账中能够看得见的isp1,ips2这两个字段对应的Isp.bus/Detail必须能够访问到，时间太久了Detail可能消失了。
     * 从IspEs搜索到了Isp.id然后关联Detail热数据。
     * 物理表上实际是bus_id名字字段的；若bus=null代表本次检验业务早就结束挺久了。
     *  @QueryInit("task.dets")
     * 验证: @OneToOne应该把字段占用空间少(字段数量少)的哪一个表当做关系维护方，估计性能好点。
     * 可能锁冲突:若用 @OneToOne(cascade = CascadeType.ALL, fetch=FetchType.LAZY) cascade={CascadeType.PERSIST,CascadeType.MERGE,CascadeType.REFRESH,CascadeType.DETACH}
     * 试验过Isp - Detail关系：还是在Isp表维护关联字段模式更好。Isp历史检验数据(简化数据和关联本次作业报告)，Detail近期检验明细(作业明细关联Isp后延伸间接关联至Report)。
     * */
    //@IndexedEmbedded(includePaths = {"id"} )
    @PropertyBinding(binder = @PropertyBinderRef(type = NodeIdBinder.class))
    @OneToOne( fetch=FetchType.LAZY)
    @JoinColumn( referencedColumnName = "ID")
    private Detail  bus;

    /**OPE_TYPE 业务大类，18种； 有的还需要配合REP_TYPE来区分 更详细的工作内容模板;
     * Isp.bsType/entrust比起来Detail.task.bsType/entrust属于长期保存历史需要，所以短期使用尽量要用Detail.task.bsType/entrust，避免使用长期存储的状态字段Isp.bsType/entrust。
     * 业务类型 OPE_TYPE：统计？模板？ TB_DICT_OPETYPE似乎没内涵36个压缩到实际28个/委托法定=
     * 法定还是委托的 是 附加属性。
     * Task底下可能为一个Eqp只能派工唯一一个业务类型(一个法定的，一个委托的)，Task底下Eqp具有唯一性。
     * 同一个Task底下的Isp的业务类型OPE_TYPE必须相同，都准备派工给同一个责任人的。
     * 同一个Task底下的Isp{Eqp}原则上，派工都是相同的检验员，校核人，协检员，联合检验部门，同时间内工作场地相同的工作。
     * LAST_ISPOPE_TYPE 1,2
     * isp1?.bsType   LAST_ISPOPE_TYPE2_NAME  lastIspopeType1  LAST_ISPOPE_TYPE1
     * Isp可以长期保存，Task一年时间统计企业活动后可能清理掉了；所以Isp/Task都存有bsType+entrust字段.
     */
    @GenericField(sortable=Sortable.YES,  aggregable=Aggregable.YES)
    @Enumerated
    private BusinessCat_Enum bsType;
    /**是委托的吗, 委托的报告出具要求可能不同于法定报告出具。
     * 同步时来自旧监察平台的,还未区分都算做法定的业务的报告。
     * 正常Isp.entrust来自Task.entrust; Task.bsType/entrust优先覆盖Isp.bsType/entrust; 【注意】一个任务bsType/entrust只能保持唯一性。Task是短期保存。长期存储看Isp；
     * */
    @GenericField(sortable=Sortable.YES,  aggregable=Aggregable.YES)
    private Boolean  entrust;

    /** 先有EQP,后来规划TASK了(1个task对1个eqp)，最后才为某task和某一个EQP去生成ISP{inspect}的;
     *一个检验ISP记录最多关联一个设备，一个设备EQP可有多个ISP检验同时间执行。
    //检验单独生成，TASK和EQP多对1的； ISP比Task更进一步，更靠近事务处理中心。
    //单个ISP检验为了某个EQP和某个TASK而生成的。主要目的推动后续的报告，管理流程，等。
    //我是多端我来维护关联关系，我的表有直接外键的存储。
    //若这改成Equipment dev报错 @OneToOne or @ManyToOne on md.specialEqp.inspect.Isp.dev references an unknown entity:
    单个Isp只能有单个Eqp; 也可能没有挂接Eqp，单独报告的就是dev=NULL 水质检测 安全阀 就没有关联Eqp的。
    报错no session：Isp->dev内省；
     一个报告只能针对一个设备，不能有多个设备号的。水质/安全阀报告/制造监检也不需要针对设备号。@Eqp若被删除：报告和Isp跟随删除！?
     旧平台的V_ISP_MAININFO视图：看到的设备号有可能是虚的=仅仅出现在Task/Isp(安全阀校验报告)。
     管道装置只能一个cod+oid, 一个任务底下不允许重复cod。
    搜索索引:若要照顾dev的统计排序需求，就必须使用自定义桥接，#还是移出去，没必要在本系统OLTP来实现此类需求，交给独立的流处理统计数据库和系统做。
     * */
    //@IndexedEmbedded(includeEmbeddedObjectId=true,includePaths={"id"} )
    @PropertyBinding(binder = @PropertyBinderRef(type = NodeIdBinder.class))
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn(name = "dev_id")
    private Eqp dev;

    /**母报告的 报告号[主体部分的编号]，合格证编号。 证书和报告上一般都会有Eqp定位方法。
     * 科室分配任务分给负责人的那时刻就明确了待检验的设备数量了，就能直接录入no号码。
     * 没挂接Eqp的报告证书的如何还原真伪？只能从no,作为外部接入查询的关键字。
     * 有挂接设备的，一般走设备定位入口，延伸查询历史的Isp/报告。
     * 水质报告这样的可以在Unit.task找到关联Isp，若Task过去时间太久了已经清理掉，就只能依赖报告号。
     * 制造监检这样的第三方想查询历史上的合格证证明，估计就得直接用合格证编号在Isp匹配了。
     * @Id 某种规则生成的关键字,全平台Key;
     * 制造监检合格证编号是证明书自身的编码，而不是出厂产品序列号。
     * 替换Report.no; 分项子报告/一份证书相关附页;
     * 有必要？做个ES索引，就是给no报告号的搜索加速？？
     * 外包的分项报告可能报告号是独立编码的。
     * 同步旧平台：LAST_ISP_REPORT1，2；没有对应状态的管理，从旧平台的导入Isp：延期检验的字段只能舍弃。
     对于旧报告的同步导入：采用 dev + no 做唯一性KEY关键字。
     报告号和证书序列号：若是本平台产生的必须保障唯一性, 外部历史数据导入的？冲突。
     旧监察平台给出的报告编号有些离谱的： "报停071211（特种设备停用封存确"   "/"  "xxx-yyy011/12/15" =?注释/多个号
     安全阀校验报告在旧平台对每一个阀门单独出报告号和单独Isp/Task_id{内容特少就一张纸够了};
     一份报告可能签发出制造多张证书的情形：规定：no后尾随-序号，来当做证书序列号，直接映射报告号；拿着证书用户能简易提取no报告号。
     一般no编码规则会隐含设备种类的标志的。
     no最好能依据检验机构ispu来区分，可指示出到底哪一个检验机构做的。
     */
    @FullTextField(analyzer = "ngram_analyzer")
    @KeywordField(name="no_sort", sortable=Sortable.YES)
    private String  no;


    /**最终给监察平台看的再做设置；报告未终结以前是用 主报告Report.stm流转状态机的检验人员列表;长期保存档案使用的。
     * 本后端不能用这个字段，应该使用具体的各个report.stm.List<User> authr这个精准的字段。#每一个分项报告检验员设置可不同的!,缺点是短期保存的stm流转数据。
     * 检验员 可做报告的人。
     * ？若是带多个分项报告的，是指主报告的检验员还是全部人：工分统计？给监察平台看的上报吗。
     * 缺省 fetch= FetchType.LAZY  ；多对多的实际都派生第三张表，不会和实体表放在一起的；
    //这地方维护多对多关系，版本升级导致中间表ISP_ISP_MEN变成ISP_USERS；？需要自己指定表名,且字段名都也改了"ISPMEN_ID"　ISP_MEN_ID？
    业务类型不同，同一个设备和可能需要派出不同的人去做，主要抓责任人，让责任人灵活配置各种人员，以及分配工作成果和权限。
     //@JsonIgnore  生成json是忽略这个属性(数据大多，全部拿到没有意义)
     在工作现场立刻增加修改检验参与人员。
     数据库分片Isp若是做了分片处理？ @ManyToMany关联表的isp_id字段用Long?明显不够吧｛复合主键city+id｝，增加路由字段辅助性质加快索引的定位:分片分布式数据库。
    */
    @ManyToMany
    @JoinTable
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL,region ="Fast")
    private List<User> ispMen;
    /*检验员姓名，有多人的以,号分割*/
    //private String ispMen;eqp

    /**报告终结+最终给监察平台看的再做设置
     * 审核人员 就一个；多个分项报告的，是指主报告的审核人。给监察看的上报吗。？=责任工程师？任务负责人;
     *可依据 checkMen.unit 来推断：到底是哪一个特检院检验机构做的检验报告。问题：User要是跳槽了那报告不能跟着变更单位啊。
     变成流程环节的审核步骤的授权操作人。
    checkMen及ispMen[]没考虑搜索需求。假如碰见：统计分析追溯的需求场景，要过滤这俩个字段？只能另外搭建独立的ES新库，考虑业务需求和功能设计的权衡；
     从本ES旧库配合关系数据库两个腾挪reindex类似地重建新ES(临时性质的)库，后续批处理作业走新库，突发或统计类型需求的处理模式,临时ES库没法做到实时同步只能对付非常规或阶段性统计需求。
    */
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn
    private User checkMen;
    /*审核人姓名*/
    //private String checkMen;

    /*没必要本地保存?。只需发给监察知晓。上一次历史检验报告登记的下检日期1,2没保存，想搞的#只能挖掘历史报告内容了:@统计分析平台可以补上去这2个非直接的字段信息。
     * 监察的主要管理字段？走单独的通道修改，检验任务 @报告+Task终结时刻 #额外修订 Eqp.nxtd1, .nxtd2？管道单元单特别。
     * 【考虑】规范模式在：主报告的report.data:文本json有两个特殊字段{"下检日期1":，"下检日期2"：}，任务终结直接提取规范报告文本字段再传递发送给监察平台的。随后设备台账从监察更新获取最新的下检日期数据到本平台。
     * 敲定下次检验日期xx1,xx2下次检验日期xx1,xx2，管道只能针对本次牵涉到的单元。
     * 定期全面，年度在线也是要分开的核算大小日期？
     * 业务记录：报告中决定的  下次检验日期1： 下次检验日期2：
     * 最近一次Isp 下结论后终结时刻，修改设备台账下次检验日期1,2; 依据来自报告json.data{下次检验日期1,2};
     * 报告内容当中必须能直接体现下次检验日期1,2; 本身是ISP1敲定.nxt1下次检验日,#需要一次改两个下次检验日.nxtd1+2?
     * 这个字段含义所指“建议下次检验(全面/年度)日期”：移入Report内容区，和监察上报检验结果的：管道单元有不同的安全等级的1级3级下次检验日期能够统一？上报监察动态决定。
     * */
//    @Deprecated
//    private Date  nextIspDate;    //移入到Eqp.nxtd1, .nxtd2; 管道单元单独针对的。

     /**检验日期:  给监察平台看的， 主报告终结时刻来设置, 下结论的时刻用单独命令设置。
     * 检验或检测的标志性日期。 还没有准备很快终结的报告，不要设置ispDate；
     * 出报告的报告号 no :最好和ispDate关联，no编号最好包括ispDate的年份比如2021代表某年检验出的报告。
     * 但是,实际的检验工作日期很长的： 开始日 --- 结束日； 直接放入报告中json/data{开始日,结束日};
     * LAST_ISP_DATE1
     * 冷数据，太久的历史数据, 最长保留20年? 检验日期已经超过1年的，移动到分离表？对于查询的影响，ES报告号搜索。
     * Isp根据日期物理上拆解两个表IspHot表+IspCold表。正常是已经完结的历史报告并且超过1年的才能移动进入IspCold。但是ID是两个表共享的。
     * IspES搜索引擎库的模型：根据no+dev+：搜索到了某个Isp;判定是否归属IspCold还是IspHot? Isp.ID分开查找。
     * 但是 Eqp PipingUnit User Task Report 等其它实体关联到了Isp{Isp+IspCold:历史报告+旧文件目录}，拆除关联减少相关可以独立存储查询和现实报告内容。
     *[旧平台可以]管道元件已终结受理{制造设备}审批通过=复制ISP和报告，没有任务,没收费发票#，只用来流转审批做报告(出具增补的商品合格证)的,压力管道元件报告书重新受理申请{repType=聚乙烯管制造监督检验项目表}。
     * */
    @GenericField(sortable=Sortable.YES,  aggregable=Aggregable.YES)
    private LocalDate  ispDate;

    /**发给监察平台的 总体结论，[监察人看的] 子报告也会有各自子结论json{};
     * Isp合格与否要看主报告落定结论；默认：子报告有一个不合格的，那么主报告应该是就算不合格的。
     * LAST_ISP_CONCLU1
     * 【冷数据判定依据】本字段非空的并且ispDate已经超期冷数据的+同时结合task?.date来判定冷数据与否。
     * 该字段有时文本较长，允许不规范的文本 ; 非Enum。 ？转回到设备台账的 boolean 合格与否判定？Report.data{json:"下结论"}合格与否？复检？不合格立刻上报监察平台。
     * 有单独途径@管理设备合格与否， #这个结论不严格。 业务层面？没必要存储合格与否boolean字段，没实际上意义。
     * 管道单元不会单独针对某个单元下结论的。
     * */
    private String  conclusion;


    /** 主体报告内容；访问报告才是Isp模型的真正目的。
     * 报告编制系统入口：流转，数据，归档的文件和或访问路径。 外部独立报告出具系统只需要一个主报告入口。流转分开需给出URL赋能。
     * 主报告，业务关注主报告，附属的分项子报告不是前端的入口连接点。
     * 工作成果输出：主报告，母+子形式的。
     * 检验是否终结直接看主报告。Isp的状态过滤要依照主报告去做总的把控！
     * 只能一个入口, 多张证书这样平行关系不支持，改为多张 子证书。
     * 封面报告,json直接链接其它分项报告;
     * 单向关联关系，底下的Set<Report>  reps才是所有的子报告。
     * 冗余字段-快捷关联模式 ! ! ! 一个集合中特别指出当前最关注的那一条,免于数组[]嵌套。
     * 主报告的角色控制--资质认证，谁有权做这份报告。
     * @ManyToOne 允许重复利用; @OneToOne保证唯一性=不能出现多个Isp都用一个Report主报告{Isp还会一起消遣同一个Report}。
     * @OneToOne 我方是关系维护者，对方也可能不知情我做了这个字段关联。
     * 报告是在责任人派工时刻添加的。 流转按钮针对Isp(等价主报告)/也能针对Isp底下某个分项报告。
     * 责任人派工按钮就确定:主报告和分项报告的初始化+第一个流程节点的授权人;1个Task底下多个Isp都一样的配置。
     * 对于本平台能够直接读取到的报告report是必须有的：本平台{前端另外组合URI的不算}无需通晓的Isp业务报告report就可=null;
     * 不管前端能够依靠no+dev信息获取和显示报告内容的场景{同步历史数据}，Report就直接忽略。
     * 【考虑】报告编制和查看报告的独立系统：依靠报告来源no报告编码的区分标记可以直接分叉访问不同平台数据来源=天然分布式处理；Report脱钩。
     * 一个Isp只会有唯一一个REP_TYPE=主报告类型; REP_TYPE和收费本来应当没有逻辑关联。
     * 因@OneToOne(cascade = CascadeType.ALL, fetch=FetchType.LAZY)删除Isp同时自动将Report附带性删除掉。
     * 旧平台对接过渡：若report为空的，就默认type=ReportType_Enum.URI会自动提供默认的URI访问设置{每个检验机构前端自主设置}，但若有特殊设置URI的必须report非空。
     * */
    @QueryInit("stm.master")
    @OneToOne(cascade = CascadeType.ALL, fetch=FetchType.LAZY)
    @JoinColumn( referencedColumnName = "ID")
    private Report  report;

    /** 报告全部内容，包含多个分项报告。多个子文件组成整体的大报告。
     * 组合大报告：分解开小报告单独流转的，最后合并主报告显示。 原始记录和正式报告一一对应分离。外部独立报告出具系统也允许单独流转子报告=标识URL。
     * 做业务的成果输出。 检验检测都会有结论证明等。
     * 全部主子报告。
     一个业务可能生成的 多个[母子]报告/合格证/手写证明{特别形式的文件/说明}， 主要目的是单独流转分项报告；
     * 缺省, fetch= FetchType.EAGER
     //有可能Report的实际数据库表还没有创建啊;
     //比旧平台多出个Report实体，旧系统是直接用Isp表。多个分项报告REP_TYPE;
     todo: 1个主/组合封面/报告，+0个或多个分项报告,带了顺序链接，打印物理页数？。
     前端页面在呈现Report报告内容json里面估计会提供关联Unit的,到底是发证书发报告给哪个单位的。
     包含封面报告 report;
     单个Task底下多个Isp都一样设置：主报告类型，分项报告数量+分项报告类型，初始化配置都一样的。但也允许修改成不一样的检验员和校核人。
     流转时期，每一个Eqp/Isp报告可有独立流转的进度和控制，但是Task终结是总的终结开关，每个Isp都终结了Task才算完成。Isp状态一致选择授权人一致的可批量=选定Task聚集流转。
     前端按钮：选定某分项报告后，+按钮【同一个Task底下多个设备Isp的同一种分项报告也同时流转】,批量+检查相似性状态。
     管道19个分项报告；锅炉最多18分项报告。
     Isp进入冷数据管理后，和Isp相关的Report主子报告也得相应移交冷存储系统，热系统就没必要存储历史冷Isp和报告,时间期限过了还会去查询旧的报告。
     关联删除：orphanRemoval是一个ORM概念，它告诉孩子是否为孤儿。一对一或一对多关系中@OneToMany(mappedBy="customer", orphanRemoval="true")
     */
    @OneToMany(mappedBy ="isp")
    private List<Report>  reps;

    //private Set<BaseReport>  reps;
    //改成安全的基础接口类－报错hibernate.AnnotationException: Use of @OneToMany or @ManyToMany targeting an unmapped class

    /**服务对象(使用单位) ：为什么保留它？ 没有Eqp dev的情况;
     上级的Task也会设置关联单位的，【特别注意】两个冗余字段的更新同步。 Isp是长期储存，Task是短期储存。
     水质报告安全阀/制造监检这样的，不依赖Task，也不依赖精确地输入报告号。还是能从服务对象(使用单位)搜索的，但是数据条数较多。
     不同同时注解IndexedEmbedded+ PropertyBinding 报错Duplicate index field definition: 'servu'
     */
    //@IndexedEmbedded(includePaths = {"id","company.name","person.name"} )
    @PropertyBinding(binder = @PropertyBinderRef(type = UnitNameBinder.class))
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn
    private Unit servu;


    //graphQL内省都是查询的getxxx;底下的Connection<SimpleReport> reps(DataFetchingEnvironment env)接替掉
    //getReps()给java代码getReportOfISP：自己用;
    public List<SimpleReport>  getReps() {
        List<SimpleReport>  parents = new ArrayList<>();
        if(reps!=null)
            parents.addAll(reps);
        return  parents;    //到了这里实际数据还是Report，并没有变化，变的只是输出或前端可看见的范围。
    }
    //套了一层interface接口
//    public SimpleReport  getReport() {
//        return  this.report;
//    }

    //需要长期保留的字段内容：冷Isp数据都会要出保存的部分字段；isp1,isp2,ispsv没有关联得到的冷数据Isp,转移存储的冷数据库表配套专门的查询接口页面。
    //需要长期保留的字段内容：但是移入冷Isp数据之后就会被丢弃的字段。
    //不需要长期存储的字段内容：检验Task期限之内有用到的字段，随着报告任务结束就可以丢弃的字段？||和Isp关联 pk 和Task关联？。

    //TODO：有些字段应该算作业务扩展信息：单独扩展检验数据实体，business;可针对特定设备的附件在检验时间才获得数据。

    //[两个归并字段]只是前端显示区别；TB_TASK_MGE.IF_HOLD_TEST'4000是否载荷试验'？
   //private Boolean test;   //.IF_WORKEQP_TEST '5000厂车是否工作装置测试'
    //private Boolean verif;  //.IF_AQ_TEST '4000是否安全监控管理系统试验验证'
    //TB_TASK_MGE.INST_PRICE 单台工程施工费（万元）
    //private Float cost;  //TB_TASK_MGE.INST_PRICE单台工程施工费（单位:万元）

    //是否使用年限到期了 IF_USE_OVERYEAR 是否使用年限到期
    //如何断定设置的/批量初始化、检验触发的？【日期+预期寿命】 是否老旧电梯：IF_OLD_DT 是否老旧电梯评估 IF_OLDDED_DT_PG
    //是否老旧电梯：IF_OLD_DT 是否老旧电梯评估 IF_OLDDED_DT_PG_q 检验历史上做了，延长EXTEND_USE_YEAR?或判定不合格/改造大修;


    /*CONS_UNIT "施工单位";土建施工单位 检验的报告才用到的；
     * 编制检验报告，要选择配套的施工单位。
     * BUILD_UNT_ID 业务申请中的施工单位 (监检)安装|改造|维修的融合称谓，SDN用的BUILD_UNT_ID，监察施工告知用；
     * 类似字段 Eqp.svp.土建施工单位:
     * [!]$施工单位 BUILD_UNT_ID BUILD_UNT_NAME；?施工告知Isp的+申请的任务？不应该放Eqp.底下,挂接错配?=>修改了改造维修安装单位?
     *
    @Deprecated
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn
    private Unit constu;
    */

    //"IF_PARAM"监察没有字段；Eqp.IF_PARAM第一位，是否限速器校验(1-5杂物电梯5进制)?=0,'1',任务生成? IF_PARAM = '1'表示需要限速器校验。
    //Eqp."ISP_TYPE""检验范畴 1:机电，2：承压，3：综合" Eqp.仅"22" Eqp内部作废？ 牵涉收费条目、部门归属/会计;
    //IF_OTHERREREP  '已报检';
    //LAST_GET_DATE 更新发证日期
    //FS_EQP_COD 起重机械附属装置设备信息 TB_CRANE_UNIT;
    //COR_DATE 整改反馈日期 如果复检派工的时间小于等待整改反馈期，则取原来的报告号TO_CHAR(A.COR_DATE, 'yyyy-mm-dd') >= TO_CHAR(SYSDATE, 'yyyy-mm-dd')\n")
    //  '超期未整改'  机电类安装监检报告，首检报告，检验结论为不合格的，因报告中没有记录整改反馈日期，

    //EFF_DATE下次定检日期; INP_BEG_DATE监检开始日期
    //IF_BATCHMAKE 制造监检 按批出具的压力容器数量TB_ISP_INSPSTOP_MGE.EQP_NUM|TB_ISP_MGE.EQP_NUM;
    //EQP_NUM  制造 压力容器数量
    //CURR_NODE,OPE_TYPE,ISP_TYPE机电1,BUSI_TYPE法定1；

    //ASSINV_FALG 0,1,2,3发票关联状态非法 发票关联及审核标志 0未关联1关联未审核2关联已审核;
    // ? 如果有关联检验合同，且为制造监检 则无需关联收费清单;
    //TASKFEE_FALG 收费审核标志 -1=未查看过收费清单,
    //ISP_CONCLU
    //F_GET_TASKFEEBYTASKID(A.TASK_ID, 0) PRICE_CNT 报告是否有收费清单标志
    //  报告收费清单状态,F_GET_TASKFEEBYTASKID(A.TASK_ID, 1) PRICE_TOTAL 报告收费清单金额;
    //LIMIT_UPLOAD_TYPE 作废字段？承压类如果是不合格数据，必须选择问题类型
    //报错TypeResolverError: Expected object type with name 'Eqp$Hibernate' to implement interface 'Equipment', but it doesn't!
    /*
    确保返回给前端__typename类型是子类Elevator等的而非基类Eqp的
    模型定义文件Isp.graphql里面type Isp{dev: Equipment}原来直接使用{dev: Eqp}来定义的，Eqp在graphql模型中的语义是除了8大类之外的其余的设备类(java中是算基类)。
    模型文件*.graphql里面Equipment才是普通含义的设备类模型。Elevator等8大类都是实现了接口/基类Equipment的。java中Elevator等8大类都派生自Eqp的。
    所以前端对于Equipment可能实际返回__typename时有9种=8大类+Eqp，另外还有EqpEs;合计有10种类型。
    * */

/*    public Equipment getDev()  {
        //通过这种方式，前端看见的__typename:还是 "Eqp"基类的,而不是Hibernate实体看到的Elevator类。
        //必须倒腾一手,否则前端收不到Eqp子类Elevator的参数。不能直接用Eqp,应该返回接口类型才可以。
        if(null==this.dev)   return null;
        //20210626 升级graphql版本boot版本后报:not initialize proxy [md.specialEqp.Eqp#118] - no Session报错；findAllTaskFilter()-isps_list()-dev:
        //20210626升级版要想让getDev返回的__typename: "Elevator"，须设置@ManyToOne(fetch = FetchType.EAGER)，得不偿失？可改成GlobalID前端解析Type类型来判定的。
      //  Object unproxiedEntity =Hibernate.unproxy(this.dev);
        //Class<?> devtype=this.dev.getClass();     上面这行确保给前端返回的是子类Elevator而不是Eqp基类的。
        //无法直接转哦；Elevator ret=unproxiedEntity instanceof Elevator ? ((Elevator) unproxiedEntity) : null;
        return (Equipment) this.dev;
    }*/

    /** 检验报告来自 哪一个特检院
     * 支持多个检验机构都能使用本平台出报告和业务流程的对所有检验机构都能共同支持。
     *加上注解 @NotNull 对于已经存在的ispu=null数据不会报错。但旧数据没有设置这个！
     * */
    //@IndexedEmbedded(includePaths = {"id"} )
    @NotNull
    @PropertyBinding(binder = @PropertyBinderRef(type = NodeIdBinder.class))
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn(name = "ispu_id")
    private Unit  ispu;     //缺省 =福建省特检院。

    /* 旧的kickstart底层中间件情形用的
    public String id() {
        return Tool.toGlobalId(this.getClass().getSimpleName(), String.valueOf(this.id));
    }
    类似于旧的kickstart情形用的 directive @dbpage on FIELD_DEFINITION 旧版本graphQL前端reps实际读这个的，不是上面getReps()的;新版本不能走这里
    public Connection<SimpleReport> reps(DataFetchingEnvironment env) {
        return new MemoryListConnection(reps.stream().collect(Collectors.toList())).get(env);
    }*/
}





//变量名只能是字母a-z A-Z，数字0-9，下划线_组合，不能包含空格，数字不能放在变量名首位,不能用语言的保留字;
//分项报告【" + REPTYPE2 + "】已流转到单人审核，请审核后并流转到 责任工程师组合 再流转;

/*
*一个设备号可以有几个Task{1:1 Isp}同时进行中;
*有分项就有多子SubISPid/多个独立REP_TYPE子报告{独立WF_TODO分项流转，单独结论}。报告模板规范当中已经明显看出是分开的子报告，纸质组合排版的。
*分项报告类型；分项单独编制的分项报告必须是103状态，主报告才可以流转到审核；
* 分项报告 子报告 IF_HAVESUBREP； TB_ISP_SUBPROJ{=Isp}  TB_TASK_TO_ISPPROJ{派工关联和关键信息,MAIN_FLAG主报告标识1：主报告;0分项报告，REP_TYPE检验项目}
* [优化和 mysql限制]
mysql单表大约在2千万条记录（4GB）下能够良好运行，经过数据库的优化后5千万条记录（10GB）下运行良好。InnoDB一个表表空间限制64TB>操作系统对文件大小的限制。
每个数据库最多可创建20亿个表，一个表允许定义1024列，每行的最大长度为8092字节（不包括文本和图像类型的长度），UTF-8字符集下，一个字符串最多需要三个字节；
Mysql每一行数据的大小不能超过65535字节；禁止在数据库中存储图片，文件等大的二进制数据；建议把BLOB或是TEXT列分离到单独的扩展表中；尽可能把所有列定义为NOT NULL；
建立索引：区分度最高的放在联合索引的最左侧（区分度=列中不同值的数量/列的总行数）；尽量把字段长度小的列放在联合索引的最左侧（因为字段长度越小，一页能存储的数据量越大，IO性能也就越好）；
使用最频繁的列放到联合索引的左侧（这样可以比较少的建立一些索引）；Mysql最多允许关联61个表查询；使用in代替or，in 的值不要超过500个；WHERE中禁止对列进行函数转换和计算；
超100万行的批量写（UPDATE、DELETE、INSERT）操作，要分批=多次进行操作；避免产生大事务操作；
每个表最大1017个列; InnoDB限制row size < 8K ？=【实际最多约支持180到200个列字段】; 一个InnoDB表最多允许64个二级索引/辅助索引；
若是NTFS文件系统+MBR分区,可支持2TB;若是2TB文件大小的：最少也能够3355万条/行的数据。硬盘格式改成GPT没有大小上限。
centos7 最好选择使用 XFS 文件系统，单个文件大小没有限制，表空间限制64TB对应10.7亿条/行的数据。
下面必须注解在实体类上头：
@SqlResultSetMapping(
        name="IspResults",
        entities={
                @EntityResult(entityClass=md.specialEqp.inspect.Isp.class, fields={
                        @FieldResult(name="id", column="id"),
                        @FieldResult(name="no", column="no"),
                        @FieldResult(name="bsType", column="bsType")}
                )},
        columns={
                @ColumnResult(name="item_name"),  其它统计结果字段 或 顺带的其它实体类的字段
                @ColumnResult(name="item_shipdate", type=java.util.Date.class)
        })      # ConstructorResult[] classes() ？  非实体类的转换DTO; #@ConstructorResult(targetClass=Employee.class,
*
* 【为了更能快速适应变动】把索引维护从源程序直接抽离掉，单独更新物理数据库索引！
和源代码分离掉，手动维护索引设置的脚本：原先是JPA注解顺带的比如@Table( uniqueConstraints ={@UniqueConstraint(name="isp_bus_id_key",columnNames={"bus_id"}),
JPA注解添加约束后，删除注解却无法自动清除，也需要额外手动删除唯一性索引的约束:  drop index xx cascade。
注意IDEA工具不能处理覆盖索引：INDEX _idx ON isp USING btree (report_id ASC) STORING (bus_id)但是工具确实显示两个字段report_id+bus_id的索引。
另外，这样@org.hibernate.annotations.Index(name="detail_bus_idx")已经Hibernate淘汰,也没法定制为 STORING(..,)。
* * */


/* @数据库修改脚本：
CREATE INDEX isp_report_id_idx ON public.isp USING btree (report_id ASC) STORING (bus_id);
CREATE UNIQUE INDEX detail_bus_ix ON public.isp USING btree (bus_id ASC) STORING (bstype, conclusion, entrust, ispdate, nextispdate, no, redoover, checkmen_id, dev_id, ispu_id, report_id, servu_id);
#CREATE UNIQUE INDEX ukdcyw0mjhjweaf1rpl08lspei4 ON public.isp USING btree (dev_id ASC, no ASC);   --初始化导入旧数据，过渡期之后就删除该索引！
       drop index ukdcyw0mjhjweaf1rpl08lspei4  cascade;
CREATE INDEX ON seipf.public.isp (dev_id) STORING (bstype, conclusion, entrust, ispdate, no, bus_id, checkmen_id, ispu_id, report_id, servu_id);
CREATE INDEX isp_dev_id_index ON public.isp USING btree (dev_id ASC);  --为了支持Eqp可删除(非集中一次性删除，重整数据？临时添加索引再集中做删除)，特地添加索引。
CREATE UNIQUE INDEX ukiqileigf7lmyo0bu2kaphc0vv ON public.isp USING btree (no ASC, ispu_id ASC);  --初始化导入旧数据,之后不要用,直接走ES搜索引擎，可删索引。
    --中间表：
    BEGIN;
    ALTER TABLE public.isp_users DROP CONSTRAINT isp_users_pkey;
    ALTER TABLE public.isp_users ADD CONSTRAINT "primary" PRIMARY KEY (isp_id, ispmen_id);
    COMMIT;
    alter table public.isp_users  drop column rowid;
* */
